﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Jt808CommandEncoder.Utils
{
    public class PackageUtil
    {
        /// <summary>
        /// package command for hex string
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="msgFlowId"></param>
        /// <param name="messageId"></param>
        /// <param name="dicParameters"></param>
        /// <returns></returns>
        public static string packageHexCommand(string deviceId, int msgFlowId, int messageId, Object parameter)
        {
            byte[] commandArr = packageBytesCommand(deviceId, msgFlowId, messageId, parameter);
            if (commandArr == null)
            {
                return null;
            }
            else
            {
                return CommonUtil.ByteToHexStr(commandArr);
            }
        }

        /// <summary>
        /// package command for byte[]
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="msgFlowId"></param>
        /// <param name="messageId"></param>
        /// <param name="dicParameters"></param>
        /// <returns></returns>
        public static byte[] packageBytesCommand(string deviceId, int msgFlowId, int messageId, Object parameter)
        {
            byte[] bodyArr = null;
            switch (messageId) {
                case 0x8103:
                    bodyArr = packageSettingCommandBody((Dictionary<Int32, Object>)parameter);
                    break;
                case 0x8104:
                    bodyArr = new byte[0];
                    break;
                case 0x8105:
                    Dictionary<int, Object> dicDeviceControlParameter = (Dictionary<int, Object>)parameter;
                    bodyArr = packageDeviceControlCommandBody(dicDeviceControlParameter.First().Key, dicDeviceControlParameter.First().Value);
                    break;
                case 0x8900:
                    Dictionary<int, string> dicTransparentParameter = (Dictionary<int, string>)parameter;
                    bodyArr = packageTransparentCommandBody(dicTransparentParameter.First().Key, dicTransparentParameter.First().Value);
                    break;
                case 0x8A00:
                    Dictionary<int, byte[]> dicRSAParameter = (Dictionary<int, byte[]>)parameter;
                    bodyArr = packagePlatformRSAPublicKeyCommandBody(dicRSAParameter.First().Key, dicRSAParameter.First().Value);
                    break;
                default:
                    return null;
            }
            //Message body with calculated check code
            List<byte> xorBodyList = new List<byte>();
            xorBodyList.AddRange(CommonUtil.ShortToBytes((short)messageId));
            xorBodyList.AddRange(CommonUtil.ShortToBytes((short)bodyArr.Length));
            xorBodyList.AddRange(getdeviceIdArr(deviceId));
            xorBodyList.AddRange(CommonUtil.ShortToBytes((short)msgFlowId));
            xorBodyList.AddRange(bodyArr);
            //get check code
            xorBodyList.Add(CommonUtil.xor(xorBodyList));
            //Escape this xor body
            byte[] escapeBodyArr = CommonUtil.escape(xorBodyList);
            //Get this full command message
            List<byte> commandList = new List<byte>();
            commandList.Add(0x7E);
            commandList.AddRange(escapeBodyArr);
            commandList.Add(0x7E);
            return commandList.ToArray();
        }
        
        /// <summary>
        /// package the setting(0x8103) command message body
        /// </summary>
        /// <param name="dicParameters"></param>
        /// <returns></returns>
        private static byte[] packageSettingCommandBody(Dictionary<Int32, Object> dicParameters)
        {
            try
            {
                List<byte> bodyList = new List<byte>();
                //Total Number of Parameters
                bodyList.Add((byte)(dicParameters.Count() & 0xFF));
                foreach (Int32 key in dicParameters.Keys)
                {
                    //Parameter ID
                    bodyList.AddRange(CommonUtil.Int32ToBytes(key));
                    switch (key)
                    {
                        //Parameter value is int32 
                        case 0x0001:
                        case 0x0018:
                        case 0x0027:
                        case 0x0029:
                        case 0x0030:
                        case 0x0055:
                        case 0x0056:
                        case 0x0080:
                        case 0xF005:
                        case 0xF018:
                        case 0xF01D:
                        case 0xF01E:
                        case 0xF022:
                        case 0xF028:
                        case 0xF02B:
                        case 0xF02E:
                        case 0xF02F:
                            //Length of Parameter
                            bodyList.Add((byte)4);
                            //Parameter value
                            bodyList.AddRange(CommonUtil.Int32ToBytes(int.Parse(dicParameters[key].ToString())));
                            break;
                        //Parameter value is string
                        case 0x0010:
                        case 0x0011:
                        case 0x0012:
                        case 0x0013:
                        case 0x0017:
                        case 0xF000:
                        case 0xF004:
                        case 0xF030:
                            string paramValue = dicParameters[key].ToString();
                            if (key == 0x0013 || key == 0x0017)
                            {
                                paramValue = CommonUtil.IpPixZore(paramValue);
                            }
                            //Length of Parameter
                            bodyList.Add((byte)paramValue.Length);
                            //Parameter value
                            bodyList.AddRange(Encoding.GetEncoding("GBK").GetBytes(paramValue));
                            break;
                        case 0xF001:
                        case 0xF002:
                        case 0xF003:
                        case 0xF00E:
                        case 0xF013:
                        case 0xF015:
                        case 0xF01C:
                        case 0xF021:
                        case 0xF024:
                        case 0xF025:
                        case 0xF026:
                        case 0xF027:
                            //Length of Parameter
                            bodyList.Add((byte)2);
                            //Parameter value
                            bodyList.AddRange(CommonUtil.ShortToBytes((short)int.Parse(dicParameters[key].ToString())));
                            break;
                        case 0xF006:
                        case 0xF007:
                        case 0xF009:
                        case 0xF00A:
                        case 0xF00B:
                        case 0xF00C:
                        case 0xF00D:
                        case 0xF00F:
                        case 0xF010:
                        case 0xF011:
                        case 0xF012:
                        case 0xF014:
                        case 0xF016:
                        case 0xF017:
                        case 0xF019:
                        case 0xF01A:
                        case 0xF01B:
                        case 0xF01F:
                        case 0xF020:
                        case 0xF023:
                        case 0xF029:
                        case 0xF02A:
                        case 0xF02C:
                        case 0xF02D:
                            //Length of Parameter
                            bodyList.Add((byte)1);
                            //Parameter value
                            bodyList.Add((byte)int.Parse(dicParameters[key].ToString()));
                            break;
                        default:
                            break;
                    }
                }
                return bodyList.ToArray();
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// package the device control(0x8105) command message body
        /// </summary>
        /// <param name="control"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private static byte[] packageDeviceControlCommandBody(int control, Object parameters) {
            try
            {
                List<byte> bodyList = new List<byte>();
                bodyList.Add((byte)control);
                switch (control)
                {
                    case 32:
                        Dictionary<string, string> dicUpgrade = (Dictionary<string, string>)parameters;
                        string strUpgrade = string.Empty;
                        if (dicUpgrade.ContainsKey("MD5"))
                        {
                            //TYPE; MODE; VERSION; PROTOCOL; URL; MD5 
                            strUpgrade = string.Format("{0};{1};{2};{3};{4};{5}", dicUpgrade["TYPE"], dicUpgrade["MODE"], dicUpgrade["VERSION"], dicUpgrade["PROTOCOL"], dicUpgrade["URL"], dicUpgrade["MD5"]);
                        }
                        else
                        {
                            //TYPE; MODE; VERSION; PROTOCOL; URL
                            strUpgrade = string.Format("{0};{1};{2};{3};{4}", dicUpgrade["TYPE"], dicUpgrade["MODE"], dicUpgrade["VERSION"], dicUpgrade["PROTOCOL"], dicUpgrade["URL"]);
                        }
                        bodyList.AddRange(Encoding.GetEncoding("GBK").GetBytes(strUpgrade));
                        break;
                    case 33:
                    case 34:
                    case 36:
                        bodyList.AddRange(Encoding.GetEncoding("GBK").GetBytes(parameters.ToString()));
                        break;
                    case 35:
                        Dictionary<string, string> dicOutput = (Dictionary<string, string>)parameters;
                        string strOutput = string.Format("{0},{1}", dicOutput["CHANNEL"], dicOutput["MODE"]);
                        bodyList.AddRange(Encoding.GetEncoding("GBK").GetBytes(strOutput));
                        break;
                    default:
                        break;
                }
                return bodyList.ToArray();
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// package Downlink Transparent Transmission (0x8900) command message body
        /// </summary>
        /// <param name="type"></param>
        /// <param name="messgae"></param>
        /// <returns></returns>
        private static byte[] packageTransparentCommandBody(int type, string messgae)
        {
            try
            {
                List<byte> bodyList = new List<byte>();
                bodyList.Add((byte)type);
                bodyList.AddRange(CommonUtil.HexStringToBytes(messgae));
                return bodyList.ToArray();
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// package Platform RSA Public Key (0x8A00) command message body
        /// </summary>
        /// <param name="e"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        private static byte[] packagePlatformRSAPublicKeyCommandBody(Int32 e, byte[] n)
        {
            try
            {
                List<byte> bodyList = new List<byte>();
                bodyList.AddRange(CommonUtil.Int32ToBytes(e));
                bodyList.AddRange(n);
                return bodyList.ToArray();
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// deviceId to byte[]
        /// </summary>
        /// <param name="deviceId"></param>
        /// <returns></returns>
        private static byte[] getdeviceIdArr(string deviceId)
        {
            deviceId = deviceId.PadLeft(12, '0');
            return CommonUtil.HexStringToBytes(deviceId);
        }
    }
}
